package viper.receive;

/*
 * @(#)LiveStream.java	1.4 01/03/13
 *
 * Copyright (c) 1999-2001 Sun Microsystems, Inc. All Rights Reserved.
 *
 * Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
 * modify and redistribute this software in source and binary code form,
 * provided that i) this copyright notice and license appear on all copies of
 * the software; and ii) Licensee does not utilize the software in a manner
 * which is disparaging to Sun.
 *
 * This software is provided "AS IS," without a warranty of any kind. ALL
 * EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
 * IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
 * NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
 * LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
 * OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
 * LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
 * INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
 * CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
 * OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGES.
 *
 * This software is not designed or intended for use in on-line control of
 * aircraft, air traffic, aircraft navigation or aircraft communications; or in
 * the design, construction, operation or maintenance of any nuclear
 * facility. Licensee represents and warrants that it will not use or
 * redistribute the Software for such purposes.
 */

/*****************************************
 * LiveStream.java
 * ----------------
 * Revision: v.1.0
 * Author: Ji Kim
 * Date Created: 12/13/2009
 * Description:
 * Last Back-Up:
 * Revision History: *Modified existing code
 * 					 *Duplex read/write capability
 * 					 *Added custom PushBufferStream functionality from continual supply of byte[] packets
 *****************************************/

import java.awt.Dimension;
import javax.media.*;
import javax.media.format.*;
import javax.media.protocol.*;
import javax.media.protocol.DataSource;
import java.io.IOException;

public class LiveStream implements PushBufferStream, Runnable {

	protected ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW);
	protected int maxDataLength;
	protected byte [] data;
	protected Dimension size;
	protected RGBFormat rgbFormat;
	protected AudioFormat audioFormat;
	protected boolean started;
	protected Thread thread;
	protected float frameRate = 20f;
	protected BufferTransferHandler transferHandler;
	protected Control [] controls = new Control[0];
	int marker = 0;
	int lastRead = 0;
	byte [][] rxbuffer;
	int rxbuffersize;
	boolean wrapped = false;

	protected boolean videoData = false;

	public LiveStream() {
		if (videoData) {
			int x, y, pos, revpos;

			size = new Dimension(320, 240);
			maxDataLength = size.width * size.height * 3;
			rgbFormat = new RGBFormat(size, maxDataLength,
					Format.byteArray,
					frameRate,
					24,
					3, 2, 1,
					3, size.width * 3,
					VideoFormat.FALSE,
					Format.NOT_SPECIFIED);

			// generate the data
			data = new byte[maxDataLength];
			pos = 0;
			revpos = (size.height - 1) * size.width * 3;
			for (y = 0; y < size.height / 2; y++) {
				for (x = 0; x < size.width; x++) {
					byte value = (byte) ((y*2) & 0xFF);
					data[pos++] = value;
					data[pos++] = 0;
					data[pos++] = 0;
					data[revpos++] = value;
					data[revpos++] = 0;
					data[revpos++] = 0;
				}
				revpos -= size.width * 6;
			}
		} else { // audio data
			/*
	    audioFormat = new AudioFormat(AudioFormat.LINEAR,
					  44100,
					  16,
					  2,
					  AudioFormat.LITTLE_ENDIAN,
					  AudioFormat.SIGNED,
					  Format.NOT_SPECIFIED,
					  Format.NOT_SPECIFIED,
					  Format.byteArray);
			 */
			audioFormat = new AudioFormat(AudioFormat.MPEG,
					44100,
					16,
					2,
					AudioFormat.BIG_ENDIAN,
					AudioFormat.SIGNED,
					8,
					16000.0,
					Format.byteArray);

			rxbuffersize = 50;
			maxDataLength = 500;
			rxbuffer = new byte[rxbuffersize][];
		}

		thread = new Thread(this);
	}

	/***************************************************************************
	 * SourceStream
	 ***************************************************************************/

	public ContentDescriptor getContentDescriptor() {
		return cd;
	}

	public long getContentLength() {
		return LENGTH_UNKNOWN;
	}

	public boolean endOfStream() {
		return false;
	}

	/***************************************************************************
	 * PushBufferStream
	 ***************************************************************************/

	int seqNo = 1;
	double freq = 2.0;

	public Format getFormat() {
		if (videoData)
			return rgbFormat;
		else
			return audioFormat;
	}

	public void update(byte[] block){
		if(marker==rxbuffersize){
			wrapped = true;
			marker=0;
		}
		if((!wrapped) || (wrapped && marker!=lastRead)){
			rxbuffer[marker]=block;
			marker++;
			//System.out.println("ADDED!+++++++++++++++++++++++++++++++++");
		}
	}

	public void read(Buffer buffer) throws IOException {
		synchronized (this) {
			if(lastRead==rxbuffersize && marker!=rxbuffersize){
				wrapped = false;
				lastRead=0;
			}

			Object outdata = buffer.getData();
			//if (outdata == null || !(outdata.getClass() == Format.byteArray) ||
			//((byte[])outdata).length < maxDataLength) {
			if(lastRead==marker){
				outdata = new byte[maxDataLength];
				//System.out.println("BAD");
			}
			else{
				outdata = new byte[rxbuffer[lastRead].length];
				//System.out.println("GOOD");
			}
			buffer.setData(outdata);
			//}
			if (videoData) {
				buffer.setFormat( rgbFormat );
				buffer.setTimeStamp( (long) (seqNo * (1000 / frameRate) * 1000000) );
				int lineNo = (seqNo * 2) % size.height;
				int chunkStart = lineNo * size.width * 3;
				System.arraycopy(data, chunkStart,
						outdata, 0,
						maxDataLength - (chunkStart));
				if (chunkStart != 0) {
					System.arraycopy(data, 0,
							outdata, maxDataLength - chunkStart,
							chunkStart);
				}
			} else {
				buffer.setFormat( audioFormat );
				buffer.setTimeStamp( (long) seqNo * 1000000000 / 8 );
				if(lastRead!=marker)
					System.arraycopy(rxbuffer[lastRead], 0, outdata, 0, rxbuffer[lastRead].length);
			}
			buffer.setSequenceNumber( seqNo );
			if(lastRead!=marker){
				buffer.setLength(rxbuffer[lastRead].length);
				lastRead++;
			}
			else
				buffer.setLength(maxDataLength);
			buffer.setFlags(32864);
			buffer.setHeader( null );
			seqNo++;
		}
		//System.out.println("READ!***************************");
	}

	public void setTransferHandler(BufferTransferHandler transferHandler) {
		synchronized (this) {
			this.transferHandler = transferHandler;
			notifyAll();
		}
	}

	void start(boolean started) {
		synchronized ( this ) {
			this.started = started;
			if (started && !thread.isAlive()) {
				thread = new Thread(this);
				thread.start();
			}
			notifyAll();
		}
	}

	/***************************************************************************
	 * Runnable
	 ***************************************************************************/

	public void run() {
		while (started) {
			synchronized (this) {
				while (transferHandler == null && started) {
					try {
						wait(1000);
					} catch (InterruptedException ie) {
					}
				} // while
			}

			if (started && transferHandler != null) {
				transferHandler.transferData(this);
				try {
					Thread.currentThread().sleep( 10 );
				} catch (InterruptedException ise) {
				}
			}
		} // while (started)
	} // run

	// Controls

	public Object [] getControls() {
		return controls;
	}

	public Object getControl(String controlType) {
		try {
			Class  cls = Class.forName(controlType);
			Object cs[] = getControls();
			for (int i = 0; i < cs.length; i++) {
				if (cls.isInstance(cs[i]))
					return cs[i];
			}
			return null;

		} catch (Exception e) {   // no such controlType or such control
			return null;
		}
	}
}

